package com.sunday.common.statemachine.study.e1_base;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineConfigurationConfigurer;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;
import org.springframework.statemachine.listener.StateMachineListener;
import org.springframework.statemachine.listener.StateMachineListenerAdapter;
import org.springframework.statemachine.state.State;

import java.util.Set;

/**
 * 配置Spring状态机，定义订单状态流转及其事件的处理。
 * 此配置类指定了订单状态及其转换规则，同时设置了状态变更时的监听。
 *
 * @author sunday
 * @since 2024/12/2
 */
@Slf4j
@Configuration  // 声明该类为Spring的配置类
@EnableStateMachine  // 启用Spring状态机功能
public class StateMachineConfig
        extends EnumStateMachineConfigurerAdapter<OrderStates, OrderEvents> {  // 继承EnumStateMachineConfigurerAdapter以便使用枚举状态和事件

    /**
     * 配置状态机的基本设置，如自动启动和监听器。
     *
     * @param config 状态机的配置器，用于设置状态机的配置
     */
    @Override
    public void configure(StateMachineConfigurationConfigurer<OrderStates, OrderEvents> config)
            throws Exception {
        config
                .withConfiguration()  // 配置状态机的基本设置
                .autoStartup(true)  // 启动时自动启动状态机
                .listener(listener());  // 注册状态机的监听器，用于监听状态变更
    }

    /**
     * 配置状态机的状态，包括初始状态和所有可用状态。
     *
     * @param states 状态机的状态配置器，用于定义状态及其属性
     */
    @Override
    public void configure(StateMachineStateConfigurer<OrderStates, OrderEvents> states)
            throws Exception {
        states
                .withStates()  // 定义状态机的状态
                .initial(OrderStates.NEW)  // 设置初始状态为NEW（订单新建）
//                .states(EnumSet.allOf(OrderStates.class))  // 包含所有定义的状态，使用EnumSet来支持枚举类型的状态集合
                .states(Set.of(OrderStates.NEW, OrderStates.PAID, OrderStates.SHIPPED))
                .end(OrderStates.COMPLETED)  // 设置结束状态为COMPLETED（订单完成）
                .end(OrderStates.CANCELED);  // 设置结束状态为CANCELED（订单取消）
    }

    /**
     * 配置状态机的状态转换规则，包括源状态、目标状态和触发事件。
     *
     * @param transitions 状态机的转换配置器，用于定义状态之间的转换
     */
    @Override
    public void configure(StateMachineTransitionConfigurer<OrderStates, OrderEvents> transitions)
            throws Exception {
        transitions
                .withExternal()  // 配置外部状态转换（即不同状态之间的转换）
                .source(OrderStates.NEW)  // 源状态：NEW
                .target(OrderStates.PAID)  // 目标状态：PAID
                .event(OrderEvents.PAY)  // 触发事件：PAY（支付事件）
                .and()  // 结束当前转换定义，开始下一个转换规则
                .withExternal()
                .source(OrderStates.PAID)  // 源状态：PAID
                .target(OrderStates.SHIPPED)  // 目标状态：SHIPPED
                .event(OrderEvents.SHIP)  // 触发事件：SHIP（发货事件）
                .and()  // 结束当前转换定义，开始下一个转换规则
                .withExternal()
                .source(OrderStates.SHIPPED)  // 源状态：SHIPPED
                .target(OrderStates.COMPLETED)  // 目标状态：COMPLETED
                .event(OrderEvents.COMPLETE);  // 触发事件：COMPLETE（订单完成事件）

        /**
         * 当一个分支没有下游的时候，则记为失败
         */
//        transitions
//                .withExternal()  // 配置外部状态转换（即不同状态之间的转换）
//                .source(OrderStates.NEW)  // 源状态：NEW
//                .target(OrderStates.PAID)  // 目标状态：PAID
//                .event(OrderEvents.PAY)  // 触发事件：PAY（支付事件）
//                .and()  // 结束当前转换定义，开始下一个转换规则
//                .withExternal()
//                .source(OrderStates.PAID)  // 源状态：PAID
//                .event(OrderEvents.SHIP)  // 触发事件：SHIP（发货事件）
//                .and()  // 结束当前转换定义，开始下一个转换规则
//                .withExternal()
//                .source(OrderStates.PAID)  // 源状态：SHIPPED
//                .target(OrderStates.COMPLETED)  // 目标状态：COMPLETED
//                .event(OrderEvents.COMPLETE);  // 触发事件：COMPLETE（订单完成事件）

    }

    /**
     * 定义一个状态机监听器，监听状态变化并进行日志记录。
     *
     * @return 一个状态机监听器实例
     */
    @Bean  // 定义为Spring的Bean，以便注入到状态机中
    public StateMachineListener<OrderStates, OrderEvents> listener() {
        return new StateMachineListenerAdapter<>() {  // 继承StateMachineListenerAdapter以简化实现
            /**
             * 状态机状态发生变化时调用此方法，输出状态变更日志。
             *
             * @param from 当前状态
             * @param to 目标状态
             */
            @Override
            public void stateChanged(State<OrderStates, OrderEvents> from, State<OrderStates, OrderEvents> to) {
                log.info("State change from {} to {}", from == null ? "无" : from.getId(), to.getId());  // 输出状态变更日志
//                log.info("State change to {}", to.getId());  // 输出状态变更日志
            }

            /**
             * 状态机触发事件时调用此方法，输出事件日志。
             *
             * @param transition 触发的转换
             */
//            @Override
//            public void transition(Transition<OrderStates, OrderEvents> transition) {
//                log.info("触发事件: {}", transition.getTrigger().getEvent());
//            }
        };
    }
}
